// DESCRIPTION: Verilator: Verilog example module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2017 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
//======================================================================

// For std::unique_ptr
#include <memory>

// Include common routines
#include <verilated.h>

// Include model header, generated from Verilating "top.v"
#include "VTOP.h"

#include <iostream>
#include <nlohmann/json.hpp>
#include <fstream>
using namespace std;
using Json = nlohmann::json;

// Legacy function required only so linking works on Cygwin and MSVC++
double sc_time_stamp() { return 0; }

int main(int argc, char** argv, char** env) {
    // This is a more complicated example, please also see the simpler examples/make_hello_c.

    // Prevent unused variable warnings
    if (false && argc && argv && env) {}

    // Create logs/ directory in case we have traces to put under it
    Verilated::mkdir("logs");

    // Construct a VerilatedContext to hold simulation time, etc.
    // Multiple modules (made later below with Vtop) may share the same
    // context to share time, or modules may have different contexts if
    // they should be independent from each other.

    // Using unique_ptr is similar to
    // "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
    // Do not instead make Vtop as a file-scope static variable, as the
    // "C++ static initialization order fiasco" may cause a crash

    // Set debug level, 0 is off, 9 is highest presently used
    // May be overridden by commandArgs argument parsing
    contextp->debug(0);

    // Randomization reset policy
    // May be overridden by commandArgs argument parsing
    contextp->randReset(2);

    // Verilator must compute traced signals
    contextp->traceEverOn(true);

    // Pass arguments so Verilated code can see them, e.g. $value$plusargs
    // This needs to be called before you create any model
    contextp->commandArgs(argc, argv);

    // Construct the Verilated model, from Vtop.h generated from Verilating "top.v".
    // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end.
    // "TOP" will be the hierarchical name of the module.
    const std::unique_ptr<VTOP> top{new VTOP{contextp.get(), "TOP"}};

    // Set Vtop's input signals
    top->reset = !0;
    top->clock = 0;

    Json root;
    ifstream config_doc("coremark_log_cooked.json");
    config_doc >> root;
    uint64_t root_num = root.size();
    unsigned int root_index = 0;
    unsigned int pred_num = 0;
    uint64_t all_time = root_num*2;
    int run_state = 0; //use enum?
    int clk3delay = 3;

    // Simulate until $finish
    while (root_index < root_num) {
        // Historical note, before Verilator 4.200 Verilated::gotFinish()
        // was used above in place of contextp->gotFinish().
        // Most of the contextp-> calls can use Verilated:: calls instead;
        // the Verilated:: versions just assume there's a single context
        // being used (per thread).  It's faster and clearer to use the
        // newer contextp-> versions.

        contextp->timeInc(1);  // 1 timeprecision period passes...
        // Historical note, before Verilator 4.200 a sc_time_stamp()
        // function was required instead of using timeInc.  Once timeInc()
        // is called (with non-zero), the Verilated libraries assume the
        // new API, and sc_time_stamp() will no longer work.

        // Toggle a fast (time/2 period) clock
        top->clock = !top->clock;
        
        top->io_idx = root.at(root_index).at("s0_final_vsetIdx").at(0);
        top->io_req_ptag = root.at(root_index).at("s1_req_ptags").at(0);
        top->io_meta_ptags_0 = root.at(root_index).at("s1_meta_ptags").at(0).at(0);
        top->io_meta_ptags_1 = root.at(root_index).at("s1_meta_ptags").at(0).at(1);
        top->io_meta_ptags_2 = root.at(root_index).at("s1_meta_ptags").at(0).at(2);
        top->io_meta_ptags_3 = root.at(root_index).at("s1_meta_ptags").at(0).at(3);
        top->io_meta_ptags_4 = root.at(root_index).at("s1_meta_ptags").at(0).at(4);
        top->io_meta_ptags_5 = root.at(root_index).at("s1_meta_ptags").at(0).at(5);
        top->io_meta_ptags_6 = root.at(root_index).at("s1_meta_ptags").at(0).at(6);
        top->io_meta_ptags_7 = root.at(root_index).at("s1_meta_ptags").at(0).at(7);

        ofstream outfile;
        outfile.open("plot_data.txt",ios::app );

        // Toggle control signals on an edge that doesn't correspond
        // to where the controls are sampled; in this example we do
        // this only on a negedge of clock, because we know
        // reset is not sampled there.
        if (top->clock) {
            if (contextp->time() > 0 && contextp->time() < 514) {
                top->reset = 1;  // Assert reset
            } else{
                top->reset = 0;  // Deassert reset
            }
            // Assign some other inputs
            if(run_state == 0){
                top->io_pd_en = 1;
                run_state = 1;
            }else if(run_state == 1){
                top->io_pd_en = 0;
                run_state = 2;
            }else{
                top->io_pd_en = 0;
                run_state = 0;
                root_index++;
            }
            int hit_rate = 0;
            if(top->io_total_cnt != 0){
                hit_rate = (top->io_hit_cnt_mul100)/(top->io_total_cnt);
            }
            if(root_index%100 == 0 && top->reset == 0){
                outfile << root_index << ' '<< hit_rate <<endl;
            }
            if(root_index%1000 == 0 && top->reset == 0){
                VL_PRINTF("[%" PRId64 "] hit_rate:%d total_cnt:0x%lx hit_cnt:0x%lx\n",
                  contextp->time(), hit_rate, top->io_total_cnt, (top->io_hit_cnt_mul100)/100);
            }
        }
        // cout << root_index << ' ' << all_time<<endl;
        top->eval();

        // Evaluate model
        // (If you have multiple models being simulated in the same
        // timestep then instead of eval(), call eval_step() on each, then
        // eval_end_step() on each. See the manual.)
        // top->eval();

        // Read outputs
        
    }

    // // Final model cleanup
    top->final();

    // Coverage analysis (calling write only after the test is known to pass)
#if VM_COVERAGE
    Verilated::mkdir("logs");
    contextp->coveragep()->write("logs/coverage.dat");
#endif

    // Return good completion status
    // Don't use exit() or destructor won't get called
    return 0;
}
